package com.simple.editor.vcs.svn;

import com.simple.editor.vcs.AbstractVcsClient;
import com.simple.editor.vcs.VcsException;
import com.simple.editor.vcs.VcsFile;
import com.simple.editor.vcs.VcsStatus;

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * {@link SvnCmdClient} provides SVN implementation of the {@link com.simple.editor.vcs.VcsClient}.
 * Based on system command utility.
 * <p/>
 * Created with IntelliJ IDEA.
 * User: vsoroka
 * Date: 05.06.13
 * Time: 13:02
 * To change this template use File | Settings | File Templates.
 */
public class SvnCmdClient extends AbstractVcsClient {

    private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
    private final String eol = System.getProperty("line.separator");
    private final String svnCmd = System.getProperty("svnCmd", "svn");
    private final Map<String, VcsStatus> statusMap;

    public SvnCmdClient() {
        statusMap = new HashMap<String, VcsStatus>(VcsStatus.values().length);
        statusMap.put("A", VcsStatus.ADDED);
        statusMap.put("C", VcsStatus.CONFLICTED);
        statusMap.put("D", VcsStatus.DELETED);
        statusMap.put("I", VcsStatus.IGNORED);
        statusMap.put("M", VcsStatus.MODIFIED);
        statusMap.put("R", VcsStatus.REPLACED);
        statusMap.put("!", VcsStatus.MISSING);
        statusMap.put("?", VcsStatus.NOT_UNDER_CONTROL);
        statusMap.put("~", VcsStatus.UNKNOWN);
        statusMap.put("X", VcsStatus.UNKNOWN);
    }

    @Override
    public void doRefreshStatus(final Collection<VcsFile> files) throws VcsException {

        // Do not use batch mode for svn status command
        // to avoid necessity to parse file names which could contain
        // e.g. cyrillic characters
        for (final VcsFile file : files) {
            final String outLine = runSvnCmd(Collections.singletonList(file), "status", "--depth=empty");
            final String[] token = WHITESPACE_PATTERN.split(outLine, 2);
            if (token.length == 2 && token[1] != null && !token[1].isEmpty()) {
                file.setStatus(parseSvnFileStatus(token[0]));
            } else {
                file.setStatus(VcsStatus.NON_MODIFIED);
            }
        }
    }

    @Override
    public void doDelete(final Collection<VcsFile> files) throws VcsException {
        runSvnCmd(files, "del", "--force");

    }

    @Override
    public void doRevert(final Collection<VcsFile> files) throws VcsException {
        runSvnCmd(files, "revert");
    }

    @Override
    public void doCommit(final Collection<VcsFile> files) throws VcsException {
        runSvnCmd(files, "commit", "--message", "commit from simple editor");
    }

    @Override
    public void doUpdate(final Collection<VcsFile> files) throws VcsException {
        runSvnCmd(files, "update");
    }

    private String runSvnCmd(final Collection<VcsFile> files, final String cmd, final String... extraParams) throws VcsException {

        Scanner errScan = null;
        Scanner outScan = null;

        try {
            final List<String> cmdList = new ArrayList<String>();

            cmdList.add(svnCmd);
            cmdList.add(cmd);
            cmdList.add("--non-interactive");

            Collections.addAll(cmdList, extraParams);

            for (final VcsFile file : files) {
                if (file.getStatus() != VcsStatus.NOT_UNDER_CONTROL) {
                    cmdList.add(file.getPath());
                }
            }

            final Process process = Runtime.getRuntime().exec(cmdList.toArray(new String[cmdList.size()]));

            errScan = new Scanner(process.getErrorStream());
            outScan = new Scanner(process.getInputStream());

            final StringBuilder buf = new StringBuilder();
            if (errScan.hasNext()) {
                while (errScan.hasNextLine()) {
                    buf.append(errScan.nextLine()).append(eol);
                }
                throw new VcsException(buf.toString());
            } else if (outScan.hasNext()) {
                while (outScan.hasNextLine()) {
                    buf.append(outScan.nextLine()).append(eol);
                }
            }

            return buf.toString();
        } catch (IOException e) {
            throw new VcsException(e);
        } finally {
            if (errScan != null) {
                errScan.close();
            }
            if (outScan != null) {
                outScan.close();
            }
        }
    }

    private VcsStatus parseSvnFileStatus(final String status) {
        VcsStatus vcsState = statusMap.get(status);
        if (vcsState == null) {
            vcsState = VcsStatus.UNKNOWN;
        }
        return vcsState;
    }
}
